#include "util.h"
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <sys/socket.h>

/*将文件描述符设置为非阻塞*/
int setnonblocking(int fd)
{
	int old_option = fcntl(fd, F_GETFL);
	int new_option = old_option | O_NONBLOCK;
	fcntl(fd, F_SETFL, new_option);
	return old_option;
}

/*将文件描述符fd上的EPOLLIN注册到epollfd指示的epoll内核事件表中。 参数enable_et 指定是否对fd采用ET模式*/
void addfd(int epollfd, int fd, int enable_et)
{
	struct epoll_event event;
	event.data.fd = fd;
	event.events  = EPOLLIN;
	if(enable_et){
		event.events |= EPOLLET;
	}
	epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event );
	setnonblocking(fd);
}

/*ET 模式的工作流程*/
void et(struct epoll_event* events, int number, int epollfd, int listenfd)
{
	char buf[1024];
	int i=0;
	for(i =0; i<number; i++){
		int sockfd = events[i].data.fd;
		if(sockfd == listenfd){
			struct sockaddr_in client_address;
			socklen_t client_addrlength = sizeof(client_address);
			int connfd = accept(listenfd, (struct sockaddr* )&client_address, &client_addrlength);
			
			addfd(epollfd, connfd, true);/*对connfd开启ET模式*/
		}else if(events[i].events & EPOLLIN){
			/*这段代码不会被重复触发，所以我们循环读取数据，以确保把socket缓冲区的数据全部读取*/
			printf("Event trigger once !!!\n");
			while(1){
				memset(buf, 0, 1024);
				int ret  = recv(sockfd, buf, 1024-1, 0);
				if(ret < 0){
					/*对于非阻塞IO，下面的事件成立标识数据已经全部读取完毕。此后，epoll就能再次触发sockfd上的sockfd的EPOLLIN事件，以驱动下一次读操作*/
					if((errno == EAGAIN) || (errno == EWOULDBLOCK)){
						printf("read later\n");
						break;
					}
					close(sockfd);
					break;
				}else if(ret == 0){
					close(sockfd);
				}else{
					printf("get %d bytes of content: %s\n", ret, buf);
				}
				
			}
		}else{
			printf("something else happen\n");
		}
		
	}
	
}

void setReuseAddr(int sockfd, bool on) {
    int optval = on ? 1 : 0;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
}

void setTcpNoDelay(int sockfd, bool on) {
	int optval = on ? 1 : 0;
	setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof optval);
}

void setKeepAlive(int sockfd, bool on) {
	int optval = on ? 1 : 0;
	setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof optval);

	int keepidle = 10; // 如该连接在60秒内没有任何数据往来,则进行探测
	int keepinterval = 5; // 探测时发包的时间间隔为5 秒
	int keepcount = 2; // 探测尝试的次数。如果第1次探测包就收到响应了,则后2次的不再发。
	setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle ));
	setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval ));
	setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount ));
}

int getTid() {
    std::stringstream ss;
    ss << std::this_thread::get_id();
    return atoi(ss.str().c_str());
}